﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

using System.IO;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using log4net;

namespace Updater.WCF.Service
{
    // NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in Web.config and in the associated .svc file.
    public class Service : IService
    {

        private static readonly ILog log = LogManager.GetLogger(typeof(Service));

        /// <summary>
        /// Returns hashcode of a file on the download server.
        /// </summary>
        /// <param name="downloadedFilename">Name of downloaded file</param>
        /// <returns>Hashcode</returns>
        public string GetFileHashCode(string downloadedFilename)
        {
            string hashCode = "***";

            try
            {
                using (HashAlgorithm hashAlgorithm = new SHA512Managed())
                {
                    using (FileStream myFile = File.Open(Path.Combine(Properties.Settings.Default.FileDropPath, downloadedFilename), FileMode.Open, FileAccess.Read))
                    {
                        hashCode = BitConverter.ToString(hashAlgorithm.ComputeHash(myFile));
                    }
                }
            }
            catch (Exception exception)
            {
                log.Error("Error getting the hash code of the file",exception);
                // Unhandled exception, such as a bad filename, will return an invalid hashcode
            }

            return hashCode;
        }

        
        /// <summary>
        /// Finds name of update
        /// </summary>
        /// <param name="oldVersionName">Number of existing version</param>
        /// <returns>Filename of newer version or "", if no update is available</returns>
        public string NameOfNewerVersion(string oldVersionName) // TODO: It is possible this method should return the next highest version ,instead of just the highest version.
        {
            string answer = "";            
            
            Match matchVersion = Regex.Match(oldVersionName, @"\.(\d*\.\d*\.\d*\.\d*)");
            string lastVersion = matchVersion.Groups[1].Value; // for testing
            Version oldVersion = new Version(lastVersion);

            matchVersion = Regex.Match(oldVersionName, @"^(.*?)\.");
            string appName = matchVersion.Groups[1].Value;

            Version nextNewestVersion = GetVersion(appName, oldVersion);
            if( nextNewestVersion > new Version(0,0,0,0) )
            {
                answer = appName + "." + nextNewestVersion.ToString() + ".msi"; //
            }

            return answer;
        }

        /// <summary>
        /// Returns a FileStream to download a file.
        /// </summary>
        /// <param name="fileName">Requested file</param>
        /// <returns>FileStream for downloading.</returns>
        public Stream GetBinaryStream(string fileName)
        {
            FileStream l_File;
            try
            {
                l_File = File.Open(Path.Combine(Properties.Settings.Default.FileDropPath,fileName), FileMode.Open, FileAccess.Read);
            }
            catch
            {
                l_File = null;
            }

            return l_File;
        }

        /// <summary>
        /// Returns true if VersionNumber is in valid VersionNumber format.
        /// </summary>
        /// <param name="VersionNumber">Full name of file</param>
        /// <returns></returns>
        public bool DoesVersionNumberMatchPattern(string VersionNumber)
        {
            return Regex.IsMatch(VersionNumber, Properties.Settings.Default.VersionNumberPattern , RegexOptions.IgnoreCase | RegexOptions.Compiled); 
        }

        /// <summary>
        /// Version of programname offered for download.
        /// </summary>
        /// <param name="appName">Application name</param>
        /// <param name="oldVersion">Version of application</param>
        /// <returns>Next newest version, or 0.0.0.0 for no version</returns>
        private Version GetVersion(string appName,Version oldVersion)
        {
            Version answer = new Version(0,0,0,0);
            Version newVersion = null;
            Match matchProgram = null;
            if (Directory.Exists(Properties.Settings.Default.FileDropPath))
            {
                foreach (string filename in Directory.GetFiles(Properties.Settings.Default.FileDropPath))
                {
                    matchProgram = Regex.Match(filename, @"^.*\\(.*?)\.");
                    // because directories become part of the filename
                    if (matchProgram.Success && appName == matchProgram.Groups[1].Value)
                    {
                        matchProgram = Regex.Match(filename, @"\.(\d*\.\d*\.\d*\.\d*)\.");
                        if (matchProgram.Success)
                        {
                            newVersion = new Version(matchProgram.Groups[1].Value);
                            if (answer == new Version(0, 0, 0, 0)) // first matching version
                            {
                                answer = newVersion;
                            }
                            else if ((newVersion > oldVersion && newVersion < answer) || (answer <= oldVersion && newVersion > answer)) // next highest version
                            {
                                answer = newVersion;
                            }
                        }
                    }
                }
            }
            else
            {
                log.Error("File drop directory does not exists");
            }
            return answer;
        }
    }
}
